﻿
////Класс для безопасного копирования файлов из одной папки в другую.
namespace Copy
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Transactions;
    
    /// <summary>
    /// Класс создания копий файлов.
    /// </summary>
    public class CopyFile
    {
        /// <summary>
        /// Метод копирования в конечную папку.
        /// </summary>
        /// <param name="sourcePath">Путь к папке из которой происходит копирование</param>
        /// <param name="targetPath">Путь к папке в которую происходит копирование</param>
        /// <param name="copySubDirs">Булевое значение-стоит ли копировать поддиректории</param>
        /// <returns>true если успешно, false в противном случае</returns>
        public bool Copy(string sourcePath, string targetPath, bool copySubDirs)
        {
            string dirI = null;
            string tmpDir;
            if (this.CopyInTmp(sourcePath, targetPath, copySubDirs, ref dirI))
            {
                if (System.IO.Directory.Exists(targetPath))
                {
                    tmpDir = Path.Combine(System.IO.Directory.GetDirectoryRoot(targetPath),@"\t");
                    try
                    {
                        if (System.IO.Directory.Exists(tmpDir))
                        {
                            System.IO.Directory.Delete(tmpDir, true);
                        }

                        System.IO.Directory.Move(targetPath, tmpDir);
                        System.IO.Directory.Delete(tmpDir, true);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);

                        if (System.IO.Directory.Exists(dirI))
                        {
                            System.IO.Directory.Delete(dirI, true);
                        }
                       
                        return false;
                    }
                    
                }
                
                System.IO.Directory.Move(dirI, targetPath);
                return true;
            }

            System.IO.Directory.Delete(dirI, true);
            return false;
        }

        /// <summary>
        /// Метод копирования во вспомогательную папку.
        /// </summary>
        /// <param name="sourcePath">Путь к папке из которой происходит копирование</param>
        /// <param name="targetPath">Путь к папке в которую происходит копирование</param>
        /// <param name="copySubDirs">Булевое значение-стоит ли копировать поддиректории</param>
        /// <param name="dirInf">Путь к вспомогательной папке</param>
        /// <returns>true если успешно, false в противном случае</returns>
        private bool CopyInTmp(string sourcePath, string targetPath, bool copySubDirs,ref string dirInf)
        {
            string dirI;
            long lengthDir = 0;
            DriveInfo driveInfo;

            if (sourcePath == string.Empty)
            {
                throw new ArgumentNullException();
            }

            if (targetPath == string.Empty)
            {
                throw new ArgumentNullException();
            }

            if (System.IO.Directory.Exists(sourcePath))
            {
                DirectoryInfo dir = new DirectoryInfo(sourcePath);
                DirectoryInfo[] dirs = dir.GetDirectories();

                if (!dir.Exists)
                {
                    throw new DirectoryNotFoundException(
                        "Source directory does not exist or could not be found: "
                        + sourcePath);
                }

                FileInfo[] files = dir.GetFiles();

                foreach (FileInfo file in files)
                {
                    lengthDir += file.Length;
                }

                dirI = System.IO.Path.GetPathRoot(targetPath);                
                driveInfo = new DriveInfo(dirI);
                dirInf = Path.Combine(dirI, @"\tmp");
                Directory.CreateDirectory(dirInf);                      
                        
                        try
                        {
                            if (lengthDir > driveInfo.AvailableFreeSpace)
                            {
                                throw new OutOfMemoryException("недостаточно памяти");
                            }

                            foreach (FileInfo file in files)
                            {                                
                                string temppath = Path.Combine(dirInf, file.Name);
                                try
                                {
                                    file.CopyTo(temppath, true);
                                }
                                catch (Exception)
                                {                                    
                                    break;
                                    throw;
                                }
                            }

                            if (copySubDirs)
                            {
                                foreach (DirectoryInfo subdir in dirs)
                                {
                                    string temppath = Path.Combine(dirInf, subdir.Name);
                                    this.Copy(subdir.FullName, temppath, copySubDirs);
                                }
                            }                           
                        }
                        catch (Exception)
                        {
                            throw;
                        }    
                    
                return true;
            }
            else
            {
                throw new DirectoryNotFoundException();
            }
        }
    }

public  class Program
    {
       public static void Main(string[] args)
        {
        }
    }
}